from datetime import datetime
from io import BytesIO
from types import FunctionType

import xlwt
from django import forms
from django.conf import settings
from django.shortcuts import render, redirect, HttpResponse
from django.urls import reverse, re_path
from django.utils.safestring import mark_safe

from stark.service.v1 import StarkHandler, get_m2m_text, Option
from stark.utils.pagination import Pagination
from web import models
from web.utils import ipv4_tools, queryset


class SubnetHostsAccount(object):
    """
    统计类
    """

    def __init__(self, subnet, *args, **kwargs):
        self.subnet = subnet
        self.args = args
        self.account = kwargs

    @property
    def header(self):
        header_list = [str(k) for k in self.account.keys()]
        return header_list

    @property
    def content(self):
        content_list = [v for k, v in self.account.items()]
        return content_list


def get_net_text(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return text

        subnet = getattr(obj, field)
        net_address = ipv4_tools.get_network_address(subnet)
        return str(net_address)

    return inner


def get_netmask_text(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return text

        subnet = getattr(obj, field)
        netmask = ipv4_tools.get_net_mask(subnet)
        return netmask

    return inner


def get_start_address_text(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return text
        subnet = getattr(obj, field)
        start_address = ipv4_tools.get_start_addrss(subnet)
        return start_address

    return inner


def get_end_address_text(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return text
        subnet = getattr(obj, field)
        end_address = ipv4_tools.get_end_address(subnet)
        return end_address

    return inner


class IpSubnetRecycleForm(forms.Form):
    note = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control'}), label='回收原因', required=True)


class IpSubnetAllocateForm(forms.Form):
    subnet = forms.CharField(
        label='子网',
        widget=forms.TextInput(attrs={'class': 'form-control', 'readonly': True})
    )
    service_type = forms.ChoiceField(
        label='业务类型',
        choices=[(None, '--------')],
        widget=forms.Select(attrs={'class': 'form-control'}),
        required=True
    )
    app_description = forms.CharField(
        label='应用类型描述',
        widget=forms.TextInput(attrs={'class': 'form-control'}),
        required=False
    )
    depart = forms.MultipleChoiceField(
        label='部门',
        choices=[],
        widget=forms.CheckboxSelectMultiple(attrs={'class': 'clearfix'}),
        required=True
    )
    location = forms.CharField(label='位置', widget=forms.TextInput(attrs={'class': 'form-control'}), required=True)
    responsible_person = forms.CharField(label='责任人', widget=forms.TextInput(attrs={'class': 'form-control'}),
                                         required=True)
    note = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control'}), label='分配原因', required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['service_type'].choices += models.ServiceType.objects.exclude(title__in=['待分配']).values_list('id',
                                                                                                                 'title')

        self.fields['depart'].choices += models.Depart.objects.values_list('id', 'title')


class IpSubnetDistributeHandler(StarkHandler):
    list_template = 'web/list_subnet.html'

    def list_view(self, request, *args, **kwargs):
        """
        列表页面
        :param request:
        :return:
        """
        # ##################1.批量操作 ###############
        action_list = self.get_action_list()
        action_dict = {func.__name__: func.text for func in action_list}

        if request.method == 'POST':
            action_func_name = request.POST.get('action')
            if action_func_name and action_func_name in action_dict:
                func_response = getattr(self, action_func_name)(request, *args, **kwargs)
                if func_response:
                    return func_response
        # 获取搜索条件

        search_list = self.get_search_list()
        """
        1.如果search_list没有值则不显示搜索框
        2.获取用户提交的关键字
        """
        search_value = request.GET.get('q', '')
        from django.db.models import Q
        """
        Q用于构造复杂的ORM查询条件
        """
        conn = Q()
        conn.connector = 'OR'
        if search_value:
            for item in search_list:
                conn.children.append((item, search_value))
        # 1.获取排序
        order_list = self.get_order_list()
        search_group_condition = self.get_search_group_condition(request)
        prev_queryset = self.get_queryset(request, *args, **kwargs)
        queryset = prev_queryset.filter(conn).filter(**search_group_condition).order_by(*order_list)
        # 处理分页
        all_count = queryset.count()
        query_params = request.GET.copy()
        query_params._mutable = True
        pager = Pagination(
            current_page=request.GET.get('page'),
            all_count=all_count,
            base_url=request.path_info,
            query_params=query_params,
            per_page=self.per_page
        )
        # 1.处理表头
        header_list = []
        list_display = self.get_list_display(request, *args, **kwargs)
        if list_display:
            for key_or_func in list_display:
                if isinstance(key_or_func, FunctionType):
                    header_list.append(key_or_func(self, None, True))
                else:
                    verbose_name = self.model_class._meta.get_field(key_or_func).verbose_name
                    header_list.append(verbose_name)
        else:
            header_list.append(self.model_class._meta.model_name)
        # 2.处理表格内容
        data_list = queryset[pager.start:pager.end]
        body_list = []
        for row in data_list:
            tr_list = []
            if list_display:
                for key_or_func in list_display:
                    if isinstance(key_or_func, FunctionType):
                        tr_list.append(key_or_func(self, row, False, *args, **kwargs))
                    else:
                        tr_list.append('' if getattr(row, key_or_func) == None else getattr(row, key_or_func))
            else:
                tr_list.append(row)
            body_list.append(tr_list)

        # 统计分析
        account_host_list = []
        account_header_list = []
        for row in data_list:
            if row.service_type_id == settings.UNUSE_SERVICE_TYPE:
                continue
            asset_type_queryset = models.AssetType.objects.all()
            temp = {}
            temp['子网'] = row.subnet
            for asset_type in asset_type_queryset:
                temp[asset_type.title] = models.Hosts.objects.filter(ip_subnet=row, ip_type=asset_type).count()
            temp['合计'] = models.Hosts.objects.filter(ip_subnet=row).count()
            temp['子网使用率'] = round((temp['合计'] - temp['待分配']) / temp['合计'] * 100, 2)
            subnet_hosts_account_obj = SubnetHostsAccount(row.subnet, **temp)
            account_host_list.append(subnet_hosts_account_obj)
            account_header_list = subnet_hosts_account_obj.header

        # 按钮添加
        add_btn = self.get_add_btn(request, *args, **kwargs)
        # 组合搜索
        search_group = self.get_search_group(request)
        search_group_list = []
        for option_object in search_group:
            search_group_list.append(option_object.get_queryset_or_tuple(request, self.model_class))
        return render(
            request,
            self.list_template or 'stark/list.html',
            {
                'body_list': body_list,
                'header_list': header_list,
                'pager': pager,
                'add_btn': add_btn,
                'search_list': search_list,
                'search_value': search_value,
                'action_dict': action_dict,
                'search_group_row_list': search_group_list,
                'account_host_list': account_host_list,
                'account_header_list': account_header_list,
            }
        )

    def display_distribute_record(self, obj=None, is_header=None, *args, **kwargs):
        """
        显示编辑按钮
        :return:
        """
        if is_header:
            return '分配记录'
        record_url = reverse('stark:web_ipsubnetrecord_list', kwargs={'subnet_id': obj.id})
        return mark_safe("<a href='%s'>分配记录</a>" % record_url)

    def display_subnet_hosts(self, obj=None, is_header=None, *args, **kwargs):
        """
        查看主机表
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if is_header:
            return '主机表'
        record_url = reverse('stark:web_hosts_subnet_list', kwargs={'subnet_id': obj.id})
        return mark_safe("<a href='%s'>主机表</a>" % record_url)

    list_display = [
        StarkHandler.display_checkbox,
        get_net_text('网络地址', 'subnet'),
        get_netmask_text('子网掩码', 'subnet'),
        get_start_address_text('起始地址', 'subnet'),
        get_end_address_text('结束地址', 'subnet'),
        get_m2m_text('使用单位', 'depart'),
        'service_type',
        'location',
        'app_description',
        'responsible_person',
        'last_change_date',
        display_distribute_record,
        display_subnet_hosts,
    ]

    model_form_class = IpSubnetAllocateForm

    def get_queryset(self, request, *args, **kwargs):
        current_user_id = request.session['user_info']['user_id']
        user_object = models.UserInfo.objects.filter(id=current_user_id).first()
        subnet_queryset = queryset.get_subnet_queryset()
        if not user_object.is_depart:
            return subnet_queryset
        subnet_queryset = subnet_queryset.filter(depart=user_object.depart).exclude(
            service_type_id=settings.UNUSE_SERVICE_TYPE)
        return subnet_queryset

    order_list = ['subnet_num']

    # search_group = [Option('depart'), Option('service_type'), Option('ip_network')]

    def get_search_group(self, request):
        current_user_id = request.session['user_info']['user_id']
        current_user_obj = models.UserInfo.objects.filter(pk=current_user_id).first()
        if not current_user_obj.is_depart:
            return [Option('depart'), Option('service_type'), Option('ip_network')]
        return [Option('service_type'), ]

    search_list = ['subnet__contains', 'location__contains', 'responsible_person__contains',
                   'app_description__contains']

    def get_add_btn(self, request, *args, **kwargs):
        return None

    def display_allocate_and_recycle(self, obj=None, is_header=None, *args, **kwargs):
        """
        同时显分配与回收按钮
        :param obj:
        :param is_header:
        :return:
        """
        if is_header:
            return '子网操作'
        tpl = "<a href='%s'>分配</a> | <a href='%s'>回收</a>" % (
            self.reverse_allocate_url(pk=obj.id), self.reverse_recycle_url(pk=obj.id)
        )
        return mark_safe(tpl)

    def display_allocate(self, obj=None, is_header=None, *args, **kwargs):
        """
        显示分配按钮
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if is_header:
            return '子网操作'
        return mark_safe("<a href='%s'>分配</a>" % self.reverse_allocate_url(pk=obj.id))

    def display_recycle(self, obj=None, is_header=None, *args, **kwargs):
        """
        显示分配按钮
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if is_header:
            return '子网操作'
        return mark_safe("<a href='%s'>回收</a>" % self.reverse_recycle_url(pk=obj.id))

    def reverse_allocate_url(self, *args, **kwargs):
        return self.reverse_commons_url(self.get_url_name('allocate'), *args, **kwargs)

    def reverse_recycle_url(self, *args, **kwargs):
        return self.reverse_commons_url(self.get_url_name('recycle'), *args, **kwargs)

    def get_list_display(self, request, *args, **kwargs):
        """
        预留列表页面显示字段的钩子函数
        :return:
        """
        value = []
        if self.list_display:
            value.extend(self.list_display)
            permission_dict = request.session.get(settings.MOBILEDJ_PERMISSION_SESSION_KEY)
            if self.get_url_name('allocate') in permission_dict and self.get_url_name('recycle') in permission_dict:
                value.append(type(self).display_allocate_and_recycle)
            elif self.get_url_name('allocate') in permission_dict:
                value.append(type(self).display_allocate)
            elif self.get_url_name('recycle') in permission_dict:
                value.append(type(self).display_recycle)
        return value

    def action_multi_export(self, request, *args, **kwargs):
        pk_list = request.POST.getlist('pk')
        now_time = datetime.now().strftime('%Y-%m-%d')
        # 设置HTTPResponse的类型
        response = HttpResponse(content_type='application/vnd.ms-excel')
        response['Content-Disposition'] = 'attachment;filename=%s.xls' % now_time
        # 创建一个文件对象
        wb = xlwt.Workbook(encoding='utf8')
        # 创建一个sheet对象
        sheet = wb.add_sheet('sheet1')

        # 设置文件头的样式,这个不是必须的可以根据自己的需求进行更改

        # 写入文件标题
        sheet.write(0, 0, '序号')
        sheet.write(0, 1, '网络地址')
        sheet.write(0, 2, '子网掩码')
        sheet.write(0, 3, '起始地址')
        sheet.write(0, 4, '结束地址')
        sheet.write(0, 5, '网关')
        sheet.write(0, 6, '使用单位')
        sheet.write(0, 7, '负责人')
        data_row = 1
        for pk in pk_list:
            subnet_object = models.IpSubnet.objects.filter(id=pk).first()
            subnet = subnet_object.subnet
            network_address = ipv4_tools.get_network_address(subnet)
            net_mask = ipv4_tools.get_net_mask(subnet)
            start_address = ipv4_tools.get_start_addrss(subnet)
            end_address = ipv4_tools.get_end_address(subnet)
            default_gateway = ipv4_tools.get_gateway(subnet)
            depart = [item['title'] for item in subnet_object.depart.values('title')]
            responsible_person = subnet_object.responsible_person
            # 写入数据
            # UserTable.objects.all()这个是查询条件,可以根据自己的实际需求做调整.
            # for i in UserTable.objects.all():
            # 格式化datetime
            # pri_time = i.pri_date.strftime('%Y-%m-%d')
            # oper_time = i.operating_time.strftime('%Y-%m-%d')
            sheet.write(data_row, 0, data_row)
            sheet.write(data_row, 1, network_address)
            sheet.write(data_row, 2, net_mask)
            sheet.write(data_row, 3, start_address)
            sheet.write(data_row, 4, end_address)
            sheet.write(data_row, 5, default_gateway)
            sheet.write(data_row, 6, depart)
            sheet.write(data_row, 7, responsible_person)

            data_row = data_row + 1

        # 写出到IO
        output = BytesIO()
        wb.save(output)
        # 重新定位到开始
        output.seek(0)
        response.write(output.getvalue())
        return response

    action_multi_export.text = '批量导出excel'

    action_list = [action_multi_export, ]

    def allocate_view(self, request, pk, *args, **kwargs):
        """
        子网分配页面
        :param request:
        :param pk:
        :return:
        """
        origin_url = self.reverse_list_url(*args, **kwargs)
        model_form_class = self.get_model_class_form(False, request, pk, *args, **kwargs)
        current_subnet_object = self.get_change_object(request, pk, *args, **kwargs)
        if not current_subnet_object:
            info = '要修改的数据不存在，请重新选择'
            return render(request, 'stark/hint.html', {'msg': info, 'return_url': origin_url})

        if request.method == "GET":
            form = model_form_class(initial={'subnet': current_subnet_object.subnet})
            ip_network_id = current_subnet_object.ip_network_id
            service_type_queryset = models.IpScope.objects.filter(id=ip_network_id).first().service_type. \
                values_list('id', 'title').exclude(id=settings.UNUSE_SERVICE_TYPE)
            form.fields['service_type'].choices = [(None, '--------')]
            form.fields['service_type'].choices += service_type_queryset
            return render(request, self.change_template or 'stark/change.html', {'form': form})
        form = model_form_class(data=request.POST, )

        if form.is_valid():
            current_user_id = request.session['user_info']['user_id']
            service_type = form.cleaned_data['service_type']
            departs = form.cleaned_data['depart']
            location = form.cleaned_data['location']
            app_description = form.cleaned_data['app_description']
            responsible_person = form.cleaned_data['responsible_person']
            note = form.cleaned_data['note']
            current_subnet_object.service_type_id = service_type
            current_subnet_object.location = location
            current_subnet_object.responsible_person = responsible_person
            current_subnet_object.note = note
            current_subnet_object.app_description = app_description
            current_subnet_object.depart.clear()
            for item in departs:
                current_subnet_object.depart.add(item)
            current_subnet_object.save()

            # 插入分配记录
            ip_subnet_id = pk
            ip_subnet = current_subnet_object.subnet
            operate_person = current_user_id
            subnet_object = models.IpSubnetRecord(
                ip_subnet_id=ip_subnet_id,
                subnet=ip_subnet,
                service_type_id=service_type,
                location=location,
                responsible_person=responsible_person,
                operate_person_id=operate_person,
                operate_type=1,
                note=note
            )
            subnet_object.save()
            for item in departs:
                subnet_object.depart.add(item)
            return redirect(self.reverse_list_url(*args, **kwargs))
        ip_network_id = current_subnet_object.ip_network_id
        service_type_queryset = models.IpScope.objects.filter(id=ip_network_id).first().service_type. \
            values_list('id', 'title').exclude(id=settings.UNUSE_SERVICE_TYPE)
        form.fields['service_type'].choices = [(None, '--------')]
        form.fields['service_type'].choices += service_type_queryset
        return render(request, self.change_template or 'stark/change.html', {'form': form})

    def recycle_view(self, request, pk, *args, **kwargs):
        """
        编辑页面
        :param request:
        :param pk:
        :return:
        """
        origin_url = self.reverse_list_url(*args, **kwargs)
        model_form_class = IpSubnetRecycleForm
        current_change_obj = self.get_change_object(request, pk, *args, **kwargs)
        if not current_change_obj:
            info = '要修改的数据不存在，请重新选择!'
            return render(request, 'stark/hint.html', {'msg': info, 'return_url': origin_url})
        if current_change_obj.service_type_id == settings.UNUSE_SERVICE_TYPE:
            info = '子网还未分配，不需要回收!'
            return render(request, 'stark/hint.html', {'msg': info, 'return_url': origin_url})
        if request.method == "GET":
            form = model_form_class()
            return render(request, self.change_template or 'stark/change.html', {'form': form})
        form = model_form_class(data=request.POST)
        if form.is_valid():
            note = form.cleaned_data['note']
            current_change_obj.depart.clear()
            current_change_obj.service_type_id = settings.UNUSE_SERVICE_TYPE
            current_change_obj.responsible_person = ''
            current_change_obj.location = ''
            current_change_obj.app_description = ''
            current_change_obj.save()
            current_change_obj.depart.clear()

            # 创建子网回收记录
            current_user_id = request.session['user_info']['user_id']
            models.IpSubnetRecord.objects.create(
                ip_subnet_id=pk,
                subnet=current_change_obj.subnet,
                service_type_id=settings.UNUSE_SERVICE_TYPE,
                note=note,
                operate_type=0,
                operate_person_id=current_user_id
            )
            return redirect(self.reverse_list_url(*args, **kwargs))
        return render(request, self.change_template or 'stark/change.html', {'form': form})

    def get_urls(self):
        """
        二次路由分发
        :return:
        """
        patterns = [
            re_path(r'^list/$', self.wrapper(self.list_view), name=self.get_list_url_name),
            # re_path(r'^add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
            # re_path(r'^change/(?P<pk>\d+)/$', self.wrapper(self.change_view),
            #         name=self.get_change_url_name),
            re_path(r'^allocate/(?P<pk>\d+)/$', self.wrapper(self.allocate_view),
                    name=self.get_url_name('allocate')),
            re_path(r'^recycle/(?P<pk>\d+)/$', self.wrapper(self.recycle_view),
                    name=self.get_url_name('recycle'))
            # re_path(r'^delete/(?P<pk>\d+)/$', self.wrapper(self.delete_view),
            #         name=self.get_delete_url_name)
        ]
        patterns.extend(self.extra_url())
        return patterns

    def extra_url(self):
        patterns = [
            re_path(r'^piechart/$', self.wrapper(self.display_piechart_view), name=self.get_url_name('chart')),

        ]
        return patterns

    def display_piechart_view(self, request, *args, **kwargs):
        ip_scope_queryset = models.IpScope.objects.all()
        subnet_hosts_account_object_list = []
        subnet_hosts_account_chart_object_list = []
        header_list = []
        temp = {}
        # service_type = models.ServiceType.objects.all().order_by('id')
        for item in ip_scope_queryset:
            temp['超网号'] = item.ip_net
            ip_subnet_queryset = queryset.get_subnet_queryset().filter(ip_network_id=item.id)
            temp['子网数量'] = ip_subnet_queryset.count()
            temp['待分配子网数量'] = ip_subnet_queryset.filter(service_type_id=settings.UNUSE_SERVICE_TYPE).count()
            subnet_id_list = [item.id for item in ip_subnet_queryset]
            temp['IP数量'] = models.Hosts.objects.filter(ip_subnet_id__in=subnet_id_list).count()
            temp['待分配IP数量'] = models.Hosts.objects.filter(ip_subnet_id__in=subnet_id_list,
                                                          ip_type_id=settings.UNUSE_IP_TYPE).count()
            temp['子网利用率'] = round((temp['子网数量'] - temp['待分配子网数量']) / temp['子网数量'] * 100, 2)

            # IP资源统计表数据
            subnet_hosts_account_object = SubnetHostsAccount(item.ip_net, **temp)
            header_list = subnet_hosts_account_object.header
            subnet_hosts_account_object_list.append(subnet_hosts_account_object)

            # 饼图数据
            if not item.is_chart:
                continue
            temp_chart = {}
            service_type = item.service_type.all()
            for type in service_type:
                temp_chart[type.title] = ip_subnet_queryset.filter(service_type=type).count()
            subnet_hosts_account_chart_object = SubnetHostsAccount(item.ip_net, **temp_chart)
            subnet_hosts_account_chart_object_list.append(subnet_hosts_account_chart_object)
        return render(
            request,
            'web/piechart.html',
            {
                'ip_subnet_chart': subnet_hosts_account_chart_object_list,
                'ip_subnet_statistic': subnet_hosts_account_object_list,
                'header_list': header_list
            }
        )
